package org.hzero.interfaces.common.utils;

import org.hzero.interfaces.common.annoations.KnifeValidate;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 用于PmsField注解的校验
 *
 * @author: tianhao.luo@hand-china.com 2021/6/28  上午10:04
 */
public class KnifeValidateUtil {

    private static final ReentrantLock reentrantLock = new ReentrantLock();
    /**
     * 缓存类对应的PmsField数据
     */
    private static volatile Map<String, List<Field>> cacheMap = new ConcurrentHashMap<>(16 << 1);

    /**
     * 扫描指定类上所有加了{@link KnifeValidate}注解的属性
     *
     * @param scanClass 待扫描类
     * @param <T>       泛型
     * @return 扫描出的属性
     */
    private static <T> List<Field> getAllPmsField(Class<T> scanClass) {
        String className = scanClass.getName();
        List<Field> fields = cacheMap.get(className);
        if (fields == null) {
            reentrantLock.lock();
            try {
                fields = cacheMap.get(className);
                if (fields == null) {
                    List<Field> classPmsFieldList = scanAllPmsField(scanClass);
                    cacheMap.put(className, classPmsFieldList);
                    return classPmsFieldList;
                } else {
                    return fields;
                }
            } finally {
                reentrantLock.unlock();
            }
        } else {
            return fields;
        }
    }


    /**
     * 扫描指定类上所有加了{@link KnifeValidate}注解的属性
     *
     * @param scanClass 待扫描类
     * @param <T>       泛型
     * @return 扫描出的属性
     */
    private static <T> List<Field> scanAllPmsField(Class<T> scanClass) {
        // 1. 扫描当前类及父类所有加了PmsField注解的属性
        List<Field> pmsFieldList = new ArrayList<>();

        Field[] classFieldArray = scanClass.getDeclaredFields();
        int count = 0;
        while (count < classFieldArray.length) {
            Field temp = classFieldArray[count];
            KnifeValidate knifeValidateAnnotation = temp.getAnnotation(KnifeValidate.class);
            if (knifeValidateAnnotation != null) {
                pmsFieldList.add(temp);
            }
            count++;
        }

        // 继续扫描父类
        Class<? super T> superclass = scanClass.getSuperclass();
        while (superclass != null) {
            List<Field> supperClassFieldList = scanAllPmsField(superclass);
            pmsFieldList.addAll(supperClassFieldList);
            superclass = superclass.getSuperclass();
        }

        // 返回所有的属性
        return pmsFieldList;
    }


    /**
     * 校验对象中的PmsField字段是否超长
     *
     * @param obj          待校验对象
     * @param pmsFieldList pms注解属性list
     * @return 返回null表示满足注解的配置；否则返回超长字段信息
     * @throws IllegalAccessException 非法参数访问异常
     */
    private static List<String> validatePmsField(Object obj, List<Field> pmsFieldList) throws IllegalAccessException {
        List<String> tempErrorMsgList = new ArrayList<>();

        // 如果当前对象类型是集合，需要对集合中的元素进行校验
        if (obj instanceof Collection) {
            for (Object next : (Collection<?>) obj) {
                String s = validatePmsField(next);
                if (!StringUtils.isEmpty(s)) {
                    tempErrorMsgList.add(s);
                }
            }
        }

        for (Field pmsField : pmsFieldList) {
            if (!pmsField.isAccessible()) {
                pmsField.setAccessible(true);
            }
            KnifeValidate knifeValidateAnnotation = pmsField.getAnnotation(KnifeValidate.class);
            int maxLength = knifeValidateAnnotation.maxLength();

            // 1. 校验是否必填
            Object tempFieldObj = pmsField.get(obj);
            if (knifeValidateAnnotation.required() && tempFieldObj == null) {
                String temp = knifeValidateAnnotation.title() + ":" + pmsField.getName() + "字段为必填项,请补充";
                tempErrorMsgList.add(temp);
            }

            // 2. 校验是否超长
            if (maxLength != -1 && tempFieldObj instanceof String && tempFieldObj.toString().length() > maxLength) {
                String temp = knifeValidateAnnotation.title() + ":" + pmsField.getName() + "字段传入长度为" + tempFieldObj.toString().length() + ",超过指定最大长度：" + maxLength;
                tempErrorMsgList.add(temp);
            }

            // 针对属性继续进行校验
            if (tempFieldObj != null) {
                if (tempFieldObj instanceof Collection) {
                    for (Object next : (Collection<?>) tempFieldObj) {
                        String s = validatePmsField(next);
                        if (!StringUtils.isEmpty(s)) {
                            tempErrorMsgList.add(s);
                        }
                    }
                } else {
                    String s = validatePmsField(tempFieldObj);
                    if (!StringUtils.isEmpty(s)) {
                        tempErrorMsgList.add(s);
                    }
                }
            }
        }
        return tempErrorMsgList;
    }

    /**
     * 校验对象中的PmsField字段是否超长
     *
     * @param obj 待校验对象
     * @return 返回null表示满足注解的配置；否则返回超长字段信息
     * @throws IllegalAccessException 非法参数访问异常
     */
    public static <T> String validatePmsField(T obj) throws IllegalAccessException {
        List<Field> allPmsField = getAllPmsField(obj.getClass());
        List<String> errorMsgList = validatePmsField(obj, allPmsField);
        return String.join(";", errorMsgList);
    }

}
